import numpy as np
import config.folder_and_file_names as config
from config.parameter_carrier import ParameterCarrier


class Filter:

    def __init__(self, cc: ParameterCarrier):
        self.cc = cc
        self.sensitive_state_indicator = np.array([])
        self.real_state_number = 0

        # this parameter is a threshold percentage of out degree for states.
        # state larger than that percentage will be selected.
        self.end_percentage = 0.02
        self.begin_percentage = 0.02

        self.distribution_threshold = 5

    #
    def find_sensitive_state(self, noisy_markov_matrix):
        self.real_state_number = noisy_markov_matrix.shape[0] - 2
        sensitive_out_degree_indicator = self.find_sensitive_out_degree_state(noisy_markov_matrix)#返回一个布尔数组，表示这个状态的出度总和是否超过阈值
        largest_out_degree_not_dominate_indicator = self.find_largest_out_degree_not_dominate(noisy_markov_matrix)#返回一个布尔数组，表示这个状态的出度分布是否过于集中，true表示这个状态的出度很分散，采用2阶
        extremely_large_out_degree_indicator = self.extremely_large_out_degree_sensitivity(noisy_markov_matrix)#返回一个布尔数组，表示出度极端
        begin_indicator = self.begin_sensitivity(noisy_markov_matrix)#返回一个布尔数组，表示虚拟开始状态是否过于敏感
        end_indicator = self.end_sensitivity(noisy_markov_matrix)
        final_indicator = np.logical_and(sensitive_out_degree_indicator, largest_out_degree_not_dominate_indicator)
        final_indicator = np.logical_or(final_indicator, extremely_large_out_degree_indicator)
        final_indicator = np.logical_or(final_indicator, begin_indicator)
        final_indicator = np.logical_or(final_indicator, end_indicator)
        self.sensitive_state_indicator = final_indicator
        return final_indicator

    #
    def end_sensitivity(self, matrix):
        end_distribution = matrix[:, -1]
        end_distribution = end_distribution[:-2]
        end_indicator = (end_distribution > np.sum(end_distribution) * self.end_percentage)
        return end_indicator

    #
    def begin_sensitivity(self, matrix):
        begin_distribution = matrix[-2, :]
        begin_distribution = begin_distribution[:-2]
        begin_indicator = (begin_distribution > np.sum(begin_distribution) * self.begin_percentage)#begin_percentage固定值0.02
        return begin_indicator

    #
    def extremely_large_out_degree_sensitivity(self, noisy_matrix):
        out_degrees = noisy_matrix.sum(axis=1)#按行求和
        total_degree = np.sum(out_degrees)
        threshold = total_degree * 0.02
        indicator = (out_degrees > threshold)
        indicator = indicator[:self.real_state_number]
        return indicator

    #
    def find_sensitive_out_degree_state(self, noisy_matrix):
        noisy_matrix = noisy_matrix[0: -2, 0: -2]
        out_degree_of_states = np.sum(noisy_matrix, axis=1)#按行求和，得到这个状态的出度
        amount_threshold = self.real_state_number * 1.414 / (self.cc.total_epsilon * self.cc.epsilon_partition[1])#θ1
        indicator = (out_degree_of_states > amount_threshold)
        return indicator

    #
    def find_largest_out_degree_not_dominate(self, noisy_matrix):
        indicator = np.empty(self.real_state_number, dtype=bool)
        for i in range(self.real_state_number):
            indicator[i] = False
            out_degree = noisy_matrix[i, :self.real_state_number]
            if np.sum(out_degree) > 0:
                sorted_degree = - np.sort(-out_degree)#对数组 out_degree 进行降序排序
                if sorted_degree[1] > 0:
                    if sorted_degree[0] / sorted_degree[1] < self.distribution_threshold:#固定值5，θ2
                        indicator[i] = True
                        #如果次大值大于0，并且最大值与次大值的比值小于5，则标记为敏感，采用二阶
        return indicator
